package TP2.controlador;

import java.awt.Point;
import java.util.ArrayList;


import TP2.modelo.*;

import java.util.*;

import niveles.*;
import TP2.vista.*;

import TP2.controlador.ObservadorDePuntos;

import TP2.modelo.Algo42;
import TP2.modelo.Avion;
import TP2.modelo.AvionBombardero;
import TP2.modelo.AvionCaza;
import TP2.modelo.AvionCivil;
import TP2.modelo.AvionExplorador;
import TP2.modelo.Avioneta;
import TP2.modelo.Escenario;
import TP2.modelo.HelicopteroDePolicia;
import TP2.vista.VistaAlgo42;
import TP2.vista.VistaAvioneta;
import TP2.vista.VistaBombardero;
import TP2.vista.VistaCaza;
import TP2.vista.VistaCivil;
import TP2.vista.VistaExplorador;
import TP2.vista.VistaHelicoptero;
import TP2.vista.VistaMapa;
import ar.uba.fi.algo3.titiritero.ControladorJuego;
import ar.uba.fi.algo3.titiritero.ObjetoVivo;
import ar.uba.fi.algo3.titiritero.SuperficieDeDibujo;
import ar.uba.fi.algo3.titiritero.vista.Circulo;
import ar.uba.fi.algo3.titiritero.vista.Imagen;

public class ManejadorDelJuego implements ObjetoVivo{
	
	
	Escenario escenario;
	ControladorJuego controlador;
	public ArrayList <Imagen> listaVistasAvionesEnJuego = new ArrayList<Imagen>();
	private ArrayList <Circulo> listaVistasDisparosEnemigos = new ArrayList<Circulo>();
	private ArrayList <Circulo> listaVistasDisparosDelJugador = new ArrayList<Circulo>();
	private ArrayList <Imagen> listaVistasItems = new ArrayList<Imagen>();
	VistaFondo Vfondo1;
	VistaFondo Vfondo2;
	Fondo fondo1;
	Fondo fondo2;
	
	private ArrayList <ManejadorDeNiveles> listaNiveles = new ArrayList<ManejadorDeNiveles>();
	int numeroDeNivel = 0;
	ManejadorDeNiveles nivelActual;
	
	VistaAlgo42 vistaAlgo42;
	
	
	public void crearAvionEnemigo(String tipoAvion, Point posicion){
		//Creo un avion del enemigo dependiendo del tipo quye se especifique
		//Ademas crea una vista para dibujar al mismo
						
		escenario = Escenario.getInstancia();
		
		//Instancio el avion correspondiente
		if(tipoAvion.equals("Avioneta")){
			escenario.agregarAvionAlJuego(new Avioneta(posicion));
			listaVistasAvionesEnJuego.add(new VistaAvioneta());
		}
		
		else if(tipoAvion.equals("AvionCaza")){
			escenario.agregarAvionAlJuego(new AvionCaza(posicion));
			listaVistasAvionesEnJuego.add(new VistaCaza());
		}
		else if(tipoAvion.equals("AvionBombardero")){
			escenario.agregarAvionAlJuego(new AvionBombardero(posicion));
			listaVistasAvionesEnJuego.add(new VistaBombardero());
		}
		else if(tipoAvion.equals("AvionExplorador")){
			escenario.agregarAvionAlJuego(new AvionExplorador(posicion));
			listaVistasAvionesEnJuego.add(new VistaExplorador());
		}
		else if(tipoAvion.equals("HelicopteroDePolicia")){
			escenario.agregarAvionAlJuego(new HelicopteroDePolicia(posicion));
			listaVistasAvionesEnJuego.add(new VistaHelicoptero());
		}
		else if(tipoAvion.equals("AvionCivil")){
			escenario.agregarAvionAlJuego(new AvionCivil(posicion));
			listaVistasAvionesEnJuego.add(new VistaCivil());
		}
		else if(tipoAvion.equals("AvionCazaEspecial")){
			escenario.agregarAvionAlJuego(new AvionCazaEspecial(posicion));
			listaVistasAvionesEnJuego.add(new VistaCazaEspecial());
		}

		//Le digo a la vista a que avion referencia
		if(listaVistasAvionesEnJuego.size() == 0){
			throw new Error("El avion solicitado para cargar no existe");
		}
			
		listaVistasAvionesEnJuego.get(listaVistasAvionesEnJuego.size()-1).setPosicionable(escenario.getAvionesEnJuego().get(escenario.getAvionesEnJuego().size()-1));
		
				
	}
	
	public void cargarFondo(String nombreArchivo){
		
				
		//Cargo los graficos (preciso el mismo grafico para los dos para que funcione bien)
		Vfondo1 = new VistaFondo(nombreArchivo);
		Vfondo2 = new VistaFondo(nombreArchivo);
		
		//Inicializo los fondos en la posicion correcta
		Point inicioY = new Point(0,0);
		fondo1 = new Fondo(Vfondo1.getAlturaImagen(), inicioY);
		inicioY.setLocation(0, (1)*Vfondo1.getAlturaImagen());
		fondo2 = new Fondo(Vfondo2.getAlturaImagen(), inicioY);
		
		
		//Agrego las vistas
		Vfondo1.setPosicionable(fondo1);
		controlador.agregarObjetoVivo(fondo1);			
		
		Vfondo2.setPosicionable(fondo2);
		controlador.agregarObjetoVivo(fondo2);
		
		
		
		
		
	}
	
	public ManejadorDelJuego(SuperficieDeDibujo superficieDeDibujo){
		
		escenario = Escenario.getInstancia();			
		
		/*
		 * Instancio los objetos del framework: una ventana y el controlador
		 */		
		
		controlador = new ControladorJuego(true);
		controlador.setSuperficieDeDibujo(superficieDeDibujo);
		
		/*
		 * Instancio un cuadrado para que actue como vista del mapa
		 */
		VistaMapa vistaMapa = new VistaMapa();
		vistaMapa.setPosicionable(escenario);
		
				
		//***************************************************************************
		//TODA ESTA PARTE ESTA HARCODEADA PARA PROBAR, CAMBIAR POR NIVEL
		//Esta parte la podria manejar toda el nivel (los datos harcodeados digo)
		/*
		 * 
		 * Creo al avion del jugador y le asigno una vista (solo harcodeada posicion, resto ok)
		 */
		
				
		
		Point posicion = new Point(250, 50);
		Algo42 avionDelJugador = (Algo42) escenario.agregarAlgo42(posicion);
		vistaAlgo42 = new VistaAlgo42();
		vistaAlgo42.setPosicionable(avionDelJugador);
		
		/////////////////////////////////////////////////////////////////////////////////////////
		//ACA SE AGREGAN LOS NIVELES, CON SOLO MODIFICAR ESTO ALCANZA PARA AGREGAR UN NUEVO NIVEL
		//Creo los niveles		
		listaNiveles.add(new Nivel1(this, controlador));
		listaNiveles.add(new Nivel2(this, controlador));
		listaNiveles.add(new Nivel3(this, controlador));
		listaNiveles.add(new Nivel4(this, controlador));
		listaNiveles.add(new Nivel5(this, controlador));
		listaNiveles.add(new NivelFinal(this, controlador));
		/////////////////////////////////////////////////////////////////////////////////////////
		
		
		//Cargo el primer nivel
		nivelActual = listaNiveles.get(0);
		
		controlador.agregarDibujable(vistaAlgo42);
		controlador.agregarObjetoVivo(this);
		
		for(int i=0; i < escenario.getAvionesEnJuego().size(); i++){
			controlador.agregarObjetoVivo(escenario.getAvionesEnJuego().get(i));
		}
		
		for(int i=0; i < escenario.getAvionDelJugador().size(); i++){
			controlador.agregarObjetoVivo(escenario.getAvionDelJugador().get(i));
		}		
		
		/*
		 * Agrego los objetos que actuan como vista a la lista de dibujables del controlador
		 * para que sean repintados al final de cada gameloop
		 */
		
		controlador.agregarDibujable(vistaMapa);
		controlador.agregarDibujable(Vfondo1);
		controlador.agregarDibujable(Vfondo2);
		
		//Agrego las vistas a todos lo enemigos y civiles
		for(int i=0; i < listaVistasAvionesEnJuego.size(); i++){
			controlador.agregarDibujable(listaVistasAvionesEnJuego.get(i));
		}
		
		//Agrego la vista del algo42 (reemplazable por un for si se quieren poner mas de un avion principal)			
		controlador.agregarDibujable(vistaAlgo42);
		
		controlador.agregarMouseClickObservador(vistaMapa);
		controlador.setIntervaloSimulacion(20);
		
		
	}
	
	public Circulo instanciarVistaDeMunicion(Municion municion){
		
		if(municion.getClass() == Laser.class){
			return (new VistaLaser());
		}
		else if(municion.getClass() == TorpedoSimple.class){
			return (new VistaTorpedoSimple());
		}
		else if(municion.getClass() == TorpedoRastreador.class){
			return (new VistaTorpedoRastreador());
		}
		else if(municion.getClass() == TorpedoAdaptable.class){
			return (new VistaTorpedoAdaptable());
		}
		else if(municion.getClass() == Cohete.class){
			return (new VistaCohete());
		}
		else
			return null;
		
	}
	
public Imagen instanciarVistaDeItem(Item item){
		
		if(item.getClass() == ItemArma.class){
			return (new VistaItemArmas());
		}
		else if(item.getClass() == ItemDeVida.class){
			return (new VistaItemEnergia());
		}
		
		else
			return null;
		
	}


	public void comenzar(){
		controlador.comenzarJuegoAsyn();	
	}
	
	public void detener() {
		if (controlador.isJuegoEnPausa()) controlador.setJuegoEnPausa(false);
		else controlador.setJuegoEnPausa(true);
	}
	
	public void agregarObservadorDePuntos(ObservadorDePuntos observador) {
		this.observadores.add(observador);
	}
	
	private ArrayList<ObservadorDePuntos> observadores = new ArrayList<ObservadorDePuntos>();

	private int cantidadPuntos;
	
	private void actualizarNumeroDeNivel(){
		try{
			numeroDeNivel = Math.abs((escenario.getAvionDelJugador().get(0).getPuntajeAcumulado() / escenario.getPuntajeDePasoDeNivel()));
			nivelActual = listaNiveles.get(numeroDeNivel);
			}
			catch (Error IndexOutOfBoundsException){
				System.out.print("GAME OVER!");
			}
	}
	
	private void pasarDeNivel(){
		
//		numeroDeNivel++;
		
		nivelActual.nivelReset();
		//Borro todo del escenario
		for(int i=0; i < escenario.getAvionesEnJuego().size() ; i++)
			escenario.getAvionesEnJuego().get(i).destruir();
		for(int i=0; i < escenario.getDisparosEnemigosEnJuego().size() ; i++)
			escenario.getDisparosEnemigosEnJuego().get(i).destruir();
		for(int i=0; i < escenario.getDisparosDelJugadorEnJuego().size() ; i++)
			escenario.getDisparosDelJugadorEnJuego().get(i).destruir();
		
		controlador.removerDibujable(Vfondo1);
		controlador.removerDibujable(Vfondo2);
		
		controlador.removerDibujable(vistaAlgo42);
		
		//Paso al proximo nivel
		
		this.actualizarNumeroDeNivel();

//		if(numeroDeNivel < listaNiveles.size()){
//			nivelActual = listaNiveles.get(numeroDeNivel);				
//		}		
	}
	private void perdiste(){
		
		nivelActual.nivelReset();
		//Borro todo del escenario
		for(int i=0; i < escenario.getAvionesEnJuego().size() ; i++)
			escenario.getAvionesEnJuego().get(i).destruir();
		for(int i=0; i < escenario.getDisparosEnemigosEnJuego().size() ; i++)
			escenario.getDisparosEnemigosEnJuego().get(i).destruir();
		for(int i=0; i < escenario.getDisparosDelJugadorEnJuego().size() ; i++)
			escenario.getDisparosDelJugadorEnJuego().get(i).destruir();
		
		controlador.removerDibujable(Vfondo1);
		controlador.removerDibujable(Vfondo2);
		
		controlador.removerDibujable(vistaAlgo42);
		
		nivelActual = new NivelPerdiste(this, controlador);
		//this.cargarFondo("Imagenes/FondoFinal.png");
		
	}
	
	
	public void vivir() {
		int a = escenario.vivir();
		
		
		//Hago vivir el fondo
		fondo1.vivir();
		fondo2.vivir();
		
		if(a == -1) {
			//JUEGO TERMINADO
			this.perdiste();
			
		} else if(a == 1){
			//PASA DE NIVEL
			this.pasarDeNivel();
			
		}
		
		//Reviso si hay nuevas armas en el juego y las mando a dibujar
		for(int i = listaVistasDisparosEnemigos.size(); i < escenario.getDisparosEnemigosEnJuego().size(); i++){
			listaVistasDisparosEnemigos.add(instanciarVistaDeMunicion(escenario.getDisparosEnemigosEnJuego().get(i)));
			listaVistasDisparosEnemigos.get(i).setPosicionable(escenario.getDisparosEnemigosEnJuego().get(i));
			controlador.agregarObjetoVivo(escenario.getDisparosEnemigosEnJuego().get(i));
			controlador.agregarDibujable(listaVistasDisparosEnemigos.get(i));
			
		}
		
		//Reviso si hay nuevos items en el juego y los mando a dibujar
		for(int i = listaVistasItems.size(); i < escenario.getItemsEnJuego().size(); i++){
			listaVistasItems.add(instanciarVistaDeItem(escenario.getItemsEnJuego().get(i)));
			listaVistasItems.get(i).setPosicionable(escenario.getItemsEnJuego().get(i));
			controlador.agregarObjetoVivo(escenario.getItemsEnJuego().get(i));
			controlador.agregarDibujable(listaVistasItems.get(i));
			
		}
		
		for(int i = listaVistasDisparosDelJugador.size(); i < escenario.getDisparosDelJugadorEnJuego().size(); i++){
			listaVistasDisparosDelJugador.add(instanciarVistaDeMunicion(escenario.getDisparosDelJugadorEnJuego().get(i)));
			listaVistasDisparosDelJugador.get(i).setPosicionable(escenario.getDisparosDelJugadorEnJuego().get(i));
			controlador.agregarObjetoVivo(escenario.getDisparosDelJugadorEnJuego().get(i));
			controlador.agregarDibujable(listaVistasDisparosDelJugador.get(i));
			
		}
		
		//Actualizo los puntos y la vida
		for (ObservadorDePuntos observador : this.observadores) {
			observador.seActualizaronLosPuntos(cantidadPuntos);
			observador.seActualizoLaVida(escenario.getAvionDelJugador().get(0).getNivelDeVida());
		}
		
		//Reviso a ver si hay aviones destruidos que haga falta dejar de dibujar
		for(int i=0; i < escenario.getAvionesEnJuego().size(); i++){
			if(escenario.getAvionesEnJuego().get(escenario.getAvionesEnJuego().size()- i - 1).estaDestruido() == true){
				controlador.removerDibujable(listaVistasAvionesEnJuego.get(escenario.getAvionesEnJuego().size()- i - 1));
				listaVistasAvionesEnJuego.remove(escenario.getAvionesEnJuego().size()- i - 1);
			}
		}
		
		//Reviso a ver si hay municiones del enemigo que ya no haga falta dibujar
		
		int i = 0;
		while(i < listaVistasDisparosEnemigos.size()){
			
			if(escenario.getDisparosEnemigosEnJuego().get(i).estaDestruido() == true){
				controlador.removerDibujable(listaVistasDisparosEnemigos.get(i));
				listaVistasDisparosEnemigos.remove(i);
			}
			else i++;
		}
		
		//Reviso a ver si hay items que ya no haga falta dibujar
		
		i = 0;
		while(i < listaVistasItems.size()){
			
			if(escenario.getItemsEnJuego().get(i).estaDestruido() == true){
				controlador.removerDibujable(listaVistasItems.get(i));
				listaVistasItems.remove(i);
			}
			else i++;
		}
		
		//Reviso si hay disparos del jugador que ya no haga falta dibujar.
		
		int j = 0;
		while(j < listaVistasDisparosDelJugador.size()){
			
			if(escenario.getDisparosDelJugadorEnJuego().get(j).estaDestruido() == true){
				controlador.removerDibujable(listaVistasDisparosDelJugador.get(j));
				listaVistasDisparosDelJugador.remove(j);
			}
			else j++;
		}
		
		
		//Mando a actualizar el nivel
		if(nivelActual.ActualizarNivel() == false){
			controlador.agregarDibujable(Vfondo1);
			controlador.agregarDibujable(Vfondo2);
			controlador.agregarDibujable(vistaAlgo42);
		}
		
		
		
		int nuevaCantidadPuntos = escenario.getPuntos();
		if(nuevaCantidadPuntos != this.cantidadPuntos){
			this.cantidadPuntos = nuevaCantidadPuntos;

			/*for(int i=cantidadChoques; i <= nuevaCantidadChoques; i++)
				this.controlador.getReproductorDeAudio().reproducir(new Elemento(60, 20)); */

			
			
		}
	}
	
		public void actualizarVistas(SuperficieDeDibujo superficieDeDibujo) {

			controlador.setJuegoEnPausa(true);
			escenario = Escenario.getInstancia();
			
			/////////Recupero aviones guardados/////////
            				
            //Destruimos los que estan en juego
			int cantidad = escenario.getAvionesEnJuego().size();
			for (int i = 0; i < cantidad; i++){
				escenario.getAvionesEnJuego().get(i).destruir();
			}
			//Eliminamos las vistas de los destruidos
			for(int i=0; i < escenario.getAvionesEnJuego().size(); i++){
				if(escenario.getAvionesEnJuego().get(escenario.getAvionesEnJuego().size()- i - 1).estaDestruido() == true){
					controlador.removerDibujable(listaVistasAvionesEnJuego.get(escenario.getAvionesEnJuego().size()- i - 1));
					listaVistasAvionesEnJuego.remove(escenario.getAvionesEnJuego().size()- i - 1);
				}
			}
			escenario.getAvionesEnJuego().clear();
			//Creamos los que vamos a recuperar
			ArrayList<Avion> avionesARecuperar = escenario.getInstanciaDeRecuperacion().getAvionesEnJuego();
		
			for (int i = 0; i < avionesARecuperar.size(); i++){
				Avion avionRecuperado = avionesARecuperar.get(i);
				this.crearAvionEnemigo(avionRecuperado.getTipo(), avionRecuperado.getPosicion());
				controlador.agregarObjetoVivo(escenario.getAvionesEnJuego().get(escenario.getAvionesEnJuego().size()-1));
   			    controlador.agregarDibujable(this.listaVistasAvionesEnJuego.get(escenario.getAvionesEnJuego().size()-1));
			}
			
			
			/////////Recupero el avion del jugador/////////
			//Creamos el que vamos a recuperar
			for (int i = 0; i < escenario.getInstanciaDeRecuperacion().getAvionDelJugador().size(); i++){
				AvionDelJugador avionRecuperado = escenario.getInstanciaDeRecuperacion().getAvionDelJugador().get(i);
				escenario.agregarAvionDelJugador(avionRecuperado);
				vistaAlgo42.setPosicionable(avionRecuperado);
			}
			//Eliminamos el que estaba
			escenario.getAvionDelJugador().remove(0);
			
			/////////Recupero los disparos enemigos/////////
			//Destruimos los que estan en juego
			cantidad = escenario.getDisparosEnemigosEnJuego().size();
				for (int i = 0; i < cantidad; i++){
					escenario.getDisparosEnemigosEnJuego().get(i).destruir();
				}
			//Creamos los que vamos a recuperar
			ArrayList<Municion> disparosARecuperar = escenario.getInstanciaDeRecuperacion().getDisparosEnemigosEnJuego();
			
			for (int i = 0; i < disparosARecuperar.size(); i++){
				Municion disparoRecuperado = disparosARecuperar.get(i);
				this.instanciarVistaDeMunicion(disparoRecuperado);
				escenario.agregarDisparoEnemigoAlJuego(disparoRecuperado);
			}
			//Eliminamos las vistas de los destruidos
			int i = 0;
			while(i < listaVistasDisparosEnemigos.size()){
				
				if(escenario.getDisparosEnemigosEnJuego().get(i).estaDestruido() == true){
					controlador.removerDibujable(listaVistasDisparosEnemigos.get(i));
					listaVistasDisparosEnemigos.remove(i);
				}
				else i++;
			}
			
			/////////Recupero los disparos del jugador/////////
			//Destruimos los que estan en juego
			cantidad = escenario.getDisparosDelJugadorEnJuego().size();
			for (i = 0; i < cantidad; i++){
				escenario.getDisparosDelJugadorEnJuego().get(i).destruir();
			}
			//Creamos los que vamos a recuperar
			disparosARecuperar = escenario.getInstanciaDeRecuperacion().getDisparosDelJugadorEnJuego();
		
			for (i = 0; i < disparosARecuperar.size(); i++){
				Municion disparoRecuperado = disparosARecuperar.get(i);
				this.instanciarVistaDeMunicion(disparoRecuperado);
				escenario.agregarDisparoDelJugador(disparoRecuperado);
			}
			//Eliminamos las vistas de los destruidos
			i = 0;
			while(i < listaVistasDisparosDelJugador.size()){
				
				if(escenario.getDisparosDelJugadorEnJuego().get(i).estaDestruido() == true){
					controlador.removerDibujable(listaVistasDisparosDelJugador.get(i));
					listaVistasDisparosDelJugador.remove(i);
				}
				else i++;
			}
			
			/////////Recupero los items/////////
			//Destruimos los que estan en juego
			cantidad = escenario.getItemsEnJuego().size();
			for (i = 0; i < cantidad; i++){
				escenario.getItemsEnJuego().get(i).destruir();
			}
			//Creamos los que vamos a recuperar
			ArrayList<Item> itemsARecuperar = escenario.getInstanciaDeRecuperacion().getItemsEnJuego();
		
			for (i = 0; i < itemsARecuperar.size(); i++){
				Item itemRecuperado = itemsARecuperar.get(i);
				this.instanciarVistaDeItem(itemRecuperado);
				escenario.agregarItemAlJuego(itemRecuperado);
			}
			//Eliminamos las vistas de los destruidos
			i = 0;
			while(i < listaVistasItems.size()){
				
				if(escenario.getItemsEnJuego().get(i).estaDestruido() == true){
					controlador.removerDibujable(listaVistasItems.get(i));
					listaVistasItems.remove(i);
				}
				else i++;
			}
			
			//Actualizamos el nivel
			
			this.actualizarNumeroDeNivel();			
			
			controlador.setJuegoEnPausa(false);
		}	
	}
